<?php
#
# dmBridge: a data access framework for CONTENTdm(R)
#
# Copyright © 2009, 2010, 2011 Board of Regents of the Nevada System of Higher
# Education, on behalf of the University of Nevada, Las Vegas
#

/**
 * @author Alex Dolski <alex.dolski@unlv.edu>
 * @license http://www.opensource.org/licenses/mit-license.php
 */
class DMJSONRepresentationTransformerV1 extends DMJSONRepresentationTransformer
implements DMHTTPAPIRepresentationTransformer {

	/**
	 * @var int
	 */
	protected $version = 1;

	/**
	 * @return JSON-encoded string
	 * @since 0.3
	 */
	public function nullResponse() {
		return $this->output();
	}

	/**
	 * @param DMCollection col
	 * @return string JSON-encoded string
	 */
	public function transformCollection(DMCollection $col) {
		$fields = array();
		foreach ($col->getFields() as $f) {
			if ($f->isHidden()) {
				continue;
			}
			$field = array();
			$field['name'] = $f->getName();
			$field['isControlled'] = (int) $f->isControlled();
			$field['isSearchable'] = (int) $f->isSearchable();
			$field['dc'] = $f->getDCNick();
			$field['type'] = $f->getType();
			$fields[$f->getNick()] = $field;
		}

		$this->addKey('dmCollection',
			array(
				'alias' => $col->getAlias(),
				'name' => $col->getName(),
				'num_objects' => $col->getNumObjects(),
				'overview_href' => $col->getOverviewURI()
					? (string) $col->getOverviewURI() : null,
				'description' => $col->getDescription(),
				'image_512_href' => $col->getImage512URI()
					? (string) $col->getImage512URI() : null,
				'fields' => $fields
			)
		);
		return $this->output();
	}

	/**
	 * @param array collections Array of DMCollection objects
	 * @return string JSON-encoded string
	 */
	public function transformCollections(array $collections) {
		$cols = array();
		foreach ($collections as $c) {
			if ($c instanceof DMCollection) {
				$cols[$c->getAlias()] = array(
					'href' => (string) $c->getURI(DMBridgeComponent::HTTPAPI),
					'name' => $c->getName(),
					'num_objects' => $c->getNumObjects()
				);
			}
		}
		$this->addKey('dmCollections', $cols);
		return $this->output();
	}

	/**
	 * @param DMComment comment
	 * @return string JSON-encoded string
	 */
	public function transformComment(DMComment $comment) {
		$data = array(
			'alias' => $comment->getObject()->getCollection()->getAlias(),
			'ptr' => $comment->getObject()->getPtr(),
			'href' => (string) $comment->getObject()->getURI(
					DMBridgeComponent::HTTPAPI),
			'dmComment' => array(
				'id' => $comment->getID(),
				'timestamp' => $comment->getTimestamp()->asISO8601(),
				'poster' => array(
					'name' => $comment->getName(),
					'email' => $comment->getEmail()
				),
				'body' => $comment->getValue()
			)
		);
		$this->addKey('dmObject', $data);
		return $this->output();
	}

	/**
	 * @param array comments Array of DMComment objects
	 * @param int page
	 * @param int rpp Comments per page
	 * @param int total
	 * @return XML-encoded string
	 * @since 0.3
	 */
	public function transformComments(array $comments, $page, $rpp, $total) {
		$data = array();
		foreach ($comments as $c) {
			if (!$c instanceof DMComment) {
				continue;
			}

			$data[] = array(
				'id' => $c->getID(),
				'href' => (string) $c->getURI(DMBridgeComponent::HTTPAPI)
			);
		}

		$this->addKey('page', $page);
		$this->addKey('resultsPerPage', $rpp);
		$this->addKey('numPages', ceil($total / $rpp));
		$this->addKey('total', $total);
		$this->addKey('comments', $data);
		return $this->output();
	}

	/**
	 * @param Exception ex
	 * @return JSON-encoded string
	 * @since 0.4
	 */
	public function transformException(Exception $ex) {
		$data = array();
		$data['message'] = $ex->getMessage();
		if (DMConfigIni::getInstance()->getString("dmbridge.debug_mode")) {
			$data['type'] = get_class($ex);
			$data['stack_trace'] = $ex->getTraceAsString();
		}
		$data['documentation'] = (string) DMInternalURI::getURIWithParams(
				"api/1/doc", array(), "html");

		$this->addKey('dmException', $data);
		return $this->output();
	}

	/**
	 * @param DMObject obj
	 * @return string JSON-encoded string
	 */
	public function transformObject(DMObject $obj) {
		$ds = DMDataStoreFactory::getDataStore();
		$children = array();
		foreach ($obj->getChildren() as $c) {
			$children[] = array(
				'pointer' => $c->getPtr(),
				'title' => $c->getTitle(),
				'thumbnail' => array(
					'href' => (string) $c->getThumbnailURL()
				),
				'href' => (string) $c->getURI(DMBridgeComponent::HTTPAPI)
			);
		}
		$metadata = array();
		foreach ($obj->getMetadata() as $f) {
			if (strlen($f->getValue()) < 1 || $f->isHidden()) {
				continue;
			}
			$metadata[] = array(
				'nick' => $f->getNick(),
				'name' => $f->getName(),
				'value' => $f->getValue(),
				'dc_map' => $f->getDCNick(),
				'searchable' => $f->isSearchable() ? 1 : 0,
				'controlled' => $f->isControlled() ? 1 : 0
			);
		}

		$cq = new DMCommentQuery($ds);
		$cq->addObject($obj);
		$cq->setApproved(1);
		$comments = array();
		foreach ($cq->getSearchResults() as $c) {
			$comments[] = array(
				'id' => $c->getID(),
				'href' => (string) $c->getURI(DMBridgeComponent::HTTPAPI)
			);
		}

		$tq = new DMTagQuery($ds);
		$tq->addObject($obj);
		$tq->setApproved(1);
		$tags = array();
		foreach ($tq->getSearchResultsAsObjects() as $t) {
			$tags[] = array(
				'id' => $t->getID(),
				'href' => (string) $t->getURI(DMBridgeComponent::HTTPAPI),
				'text' => $t->getValue()
			);
		}

		$data = array();
		$data['alias'] = $obj->getCollection()->getAlias();
		$data['ptr'] = $obj->getPtr();
		$data['created'] = $obj->getDateCreated()->asISO8601();
		$data['last_updated'] = $obj->getDateUpdated()->asISO8601();
		$data['is_compound'] = (int) $obj->isCompound();
		$data['web_template'] = array(
			'href' => (string) $obj->getReferenceURL()
		);
		$data['file'] = array(
			'href' => (string) $obj->getFileURL(),
			'filename' => $obj->getFilename(),
			'mime_type' => $obj->getMediaType()->__toString(),
			'height' => $obj->getHeight(),
			'width' => $obj->getWidth()
		);
		$data['image_generator'] = rtrim(DMHTTPRequest::getCurrent()->getURI(), "/")
			. DMConfigIni::getInstance()->getString("contentdm.getimage_exe.path");
		$data['image'] = array(
			'small' => (string) $obj->getImageURL(300, 300),
			'medium' => (string) $obj->getImageURL(600, 600),
			'large' => (string) $obj->getImageURL(900, 900)
		);
		$data['thumbnail'] = array(
			'href' => (string) $obj->getThumbnailURL()
		);
		$data['rating'] = array(
			'num_ratings' => $ds->getNumRatingsForObject($obj),
			'average' => $obj->getRating(100)
		);
		$data['parent'] = ($obj->getParent())
			? (string) $obj->getParent()->getURI(DMBridgeComponent::HTTPAPI)
			: null;
		$data['children'] = $children;
		$data['metadata'] = $metadata;
		$data['tags'] = $tags;
		$data['comments'] = $comments;

		$this->addKey('dmObject', $data);
		return $this->output();
	}

	/**
	 * @param DMObject obj
	 * @param int page
	 * @param int Results per page
	 * @return string JSON-encoded string
	 */
	public function transformObjectComments(DMObject $obj, $page, $rpp) {
		$comments = DMDataStoreFactory::getDataStore()
			->getApprovedCommentsForObject($obj, $page, $rpp);
		$cdata = array();
		foreach ($comments as $c) {
			if (!$c instanceof DMComment) {
				continue;
			}
			$comment = array(
				'id' => $c->getID(),
				'href' => (string) $c->getURI(DMBridgeComponent::HTTPAPI),
				'timestamp' => $c->getTimestamp()->asISO8601(),
				'poster' => array(
					'name' => $c->getName(),
					'email' => $c->getEmail()
				),
				'body' => $c->getValue()
			);
			$cdata[] = $comment;
		}

		$data = array(
			'alias' => $obj->getCollection()->getAlias(),
			'pointer' => $obj->getPtr(),
			'href' => (string) $obj->getURI(DMBridgeComponent::HTTPAPI),
			'comments' => $cdata
		);

		$this->addKey('dmObject', $data);
		return $this->output();
	}

	/**
	 * @param DMObject obj
	 * @return string JSON-encoded string
	 */
	public function transformObjectRating(DMObject $obj) {
		$ds = DMDataStoreFactory::getDataStore();
		$data = array(
			'alias' => $obj->getCollection()->getAlias(),
			'pointer' => $obj->getPtr(),
			'href' => (string) $obj->getURI(DMBridgeComponent::HTTPAPI),
			'rating' => array(
				'num_ratings' => $ds->getNumRatingsForObject($obj),
				'average' => $obj->getRating(100)
			)
		);

		$this->addKey('dmObject', $data);
		return $this->output();
	}

	/**
	 * @param DMObject obj
	 * @return string JSON-encoded string
	 */
	public function transformObjectTags(DMObject $obj) {
		$ds = DMDataStoreFactory::getDataStore();
		$tq = new DMTagQuery($ds);
		$tq->addObject($obj);
		$tq->setApproved(1);
		$tags = array();
		foreach ($tq->getSearchResultsAsObjects() as $t) {
			$tags[] = array(
				'href' => (string) $t->getURI(DMBridgeComponent::HTTPAPI),
				'text' => $t->getValue()
			);
		}

		$data = array(
			'alias' => $obj->getCollection()->getAlias(),
			'pointer' => $obj->getPtr(),
			'href' => (string) $obj->getURI(DMBridgeComponent::HTTPAPI),
			'tags' => $tags
		);

		$this->addKey('dmObject', $data);
		return $this->output();
	}

	/**
	 * @param DMObjectQuery q
	 * @return string JSON-encoded string
	 * @since 1.0
	 */
	public function transformRatings(DMObjectQuery $q) {
		// results
		$this->addKey('resultsPerPage', $q->getNumResultsPerPage());
		$this->addKey('total', $q->getNumResults());

		$data = array();
		foreach ($q->getSearchResults() as $obj) {
			$data[] = array(
				'alias' => $obj->getCollection()->getAlias(),
				'pointer' => $obj->getPtr(),
				'href' => (string) $obj->getURI(DMBridgeComponent::HTTPAPI),
				'rating' => $obj->getRating()
			);
		}
		$this->addKey('results', $data);

		return $this->output();
	}

	/**
	 * @param DMObjectQuery q
	 * @return string JSON-encoded string
	 */
	public function transformResults(DMObjectQuery $q) {
		// suggestion
		if ($q->getSuggestion()) {
			$this->addKey('suggestion', $q->getSuggestion());
		}

		// results
		$this->addKey('page', $q->getPage());
		$this->addKey('resultsPerPage', $q->getNumResultsPerPage());
		$this->addKey('numPages', $q->getNumPages());
		$this->addKey('total', $q->getNumResults());

		$data = array();
		foreach ($q->getSearchResults() as $obj) {
			$data[] = array(
				'alias' => $obj->getCollection()->getAlias(),
				'pointer' => $obj->getPtr(),
				'href' => (string) $obj->getURI(DMBridgeComponent::HTTPAPI)
			);
		}
		$this->addKey('results', $data);

		// facets
		$data = array();
		foreach ($q->getFacetTerms() as $f) {
			$data[] = array(
				'field' => $f->getField()->getNick(),
				'name' => $f->getField()->getName(),
				'count' => $f->getCount(),
				'href' => (string) $f->getURI()
			);
		}
		$this->addKey('facets', $data);

		return $this->output();
	}

	/**
	 * @return JSON-encoded string
	 * @since 0.5
	 */
	public function transformStatus() {
		// "are_available" should not be hard-coded
		$data = array(
			'institution' => array(
				'name' => DMConfigXML::getInstance()->getInstitutionName()
			),
			'comments' => array(
				'are_available' => (int) DMConfigXML::getInstance()->isCommentingEnabled(),
				'are_moderated' => (int) DMConfigXML::getInstance()->isCommentModerationEnabled()
			),
			'tags' => array(
				'are_available' => (int) DMConfigXML::getInstance()->isTaggingEnabled(),
				'are_moderated' => (int) DMConfigXML::getInstance()->isTagModerationEnabled()
			),
			'ratings' => array(
				'are_available' => (int) DMConfigXML::getInstance()->isRatingEnabled()
			),
			'version' => DMConfigXML::getInstance()->getVersion(),
			'version_sequence' => DMConfigXML::getInstance()->getVersionSequence()
		);
		$this->addKey('dmStatus', $data);
		return $this->output();
	}

	/**
	 * @param DMTag tag
	 * @return JSON-encoded string
	 * @since 0.4
	 */
	public function transformTag(DMTag $tag) {
		$data = array(
			'alias' => $tag->getObject()->getCollection()->getAlias(),
			'ptr' => $tag->getObject()->getPtr(),
			'href' => (string) $tag->getObject()->getURI(DMBridgeComponent::HTTPAPI),
			'dmTag' => array(
				'id' => $tag->getID(),
				'timestamp' => $tag->getTimestamp()->asISO8601(),
				'value' => $tag->getValue()
			)
		);
		$this->addKey('dmObject', $data);
		return $this->output();
	}

	/**
	 * @param array tags
	 * @param int page
	 * @param int rpp
	 * @param int total
	 * @return JSON-encoded string
	 * @since 0.4
	 */
	public function transformTagCounts(array $tags, $page, $rpp, $total) {
		$data = array();
		foreach ($tags as $value => $count) {
			$data[$value] = $count;
		}
		$this->addKey('page', $page);
		$this->addKey('resultsPerPage', $rpp);
		$this->addKey('numPages', ceil($total / $rpp));
		$this->addKey('total', $total);
		$this->addKey('tags', $data);
		return $this->output();
	}

	/**
	 * @param array freqs Associative array in value => count format
	 * @return string JSON-encoded string
	 */
	public function transformVocabularyFrequencies(array $freqs) {
		$data = array();
		foreach ($freqs as $term => $count) {
			$data[$term] = $count;
		}
		$this->addKey('vocabulary', $data);
		return $this->output();
	}

	/**
	 * @param array words Indexed array of strings
	 * @return string JSON-encoded string
	 */
	public function transformVocabularySuggestions(array $words) {
		$this->addKey('vocabulary', $words);
		return $this->output();
	}

	protected function addKey($key, $value) {
		if (is_null($key)) {
			$this->json_array[] = $value;
		} else {
			$this->json_array['dmBridgeResponse'][$key] = $value;
		}
	}

	protected function output() {
		$this->addKey('copyright', DMConfigXML::getInstance()->getFeedCopyright());
		$this->addKey('querySeconds', $this->getQSeconds());
		$this->addKey('version', $this->getVersion());

		// json_encode has problems with non-UTF-8 data and we cannot assume
		// the output is valid UTF-8 as of CONTENTdm version 5.3.
		// http://code.google.com/p/dmbridge/issues/detail?id=42
		$output = json_encode($this->json_array);
		if (!DMString::isUTF8($output)) {
			$output = DMString::clean($output);
		}
		return $output;
	}

}
